/**
  ETFRoot project, Anyang Normal University && IMP-CAS
  \class ETFDCTaL
  \brief a specification of ETFDCTa, with larger drift cells (10mm)
  \author SUN Yazhou, asia.rabbit@163.com
  \since 2022-04-25
  \date 2022-04-25 last modified
  \attention
  changelog
  <table>
  <tr>  <th>Date         <th>Author      <th>Description                   </tr>
  <tr>  <td>2022-04-25   <td>Asia Sun    <td>file created                  </tr>
  </table>

  \copyright Copyright (c) 2021 Anyang Normal U. && IMP-CAS with LGPLv3 LICENSE
*/

#include <cstring>
#include <TClonesArray.h>
#include <TGraph.h>
#include "ETFDCTaL.h"
#include "ETFDetectorManager.h"
#include "ETFHPTDCChannel.h"
#include "ETFDCHit.h"
#include "ETFMath.h"
#include "ETFMsg.h"
#include "ETFVTrack.h"
#include "ETFParaManager.h"
#include "ETFYaml.h"

ClassImp(ETFDCTaL);

static const double DEGREE = ETFMath::DEGREE();

ETFDCTaL::ETFDCTaL(const char *name) : ETFDCTa(name){
  NL = 40;
  // pass user trig bound parameters //
  ETFParaManager *pm = ETFParaManager::Instance();
  const YAML::Node &trigBConfig = (*pm->GetMisPara())["dcTaLTrigB"];
  if(trigBConfig){
    fTrigLB = trigBConfig[0].as<double>();
    fTrigHB = trigBConfig[1].as<double>();
  } // end if
  // fill channel id map //
  const YAML::Node &chIdConfig = (*ETFParaManager::Instance()->GetChIdPara())[name];
  int cnt = 0; memset(type, 0, sizeof(type)); // type[0-2]: [X-Y, X1-2, cableId]
  for(const auto &chid : chIdConfig){
    if(cnt && 0 == cnt%5){ type[1]++; type[2] = 0; } // X1-2
    if(cnt && 0 == cnt%10){ type[0]++; type[1] = 0; } // X-Y
    FillXYChMap(chid.as<int>());
    type[2]++; cnt++;
    if(cnt > 20) ETFMsg::Error("ETDCTaL", "ctor: cnt=%d > 20!", cnt);
  } // end for over cables
} // end ctor

ETFDCTaL::~ETFDCTaL(){}

// fill the channel map for X or Y wires
// XY, X1-X2, senseWireId
void ETFDCTaL::FillXYChMap(int chId0){
  if(chId0 < 0) return;
  for(int i = 0; i < 8; i++)
    //        chanel id           X-Y           X1-2     cableId: 0-4  wireId: 0-7
    SetChIdMap(chId0+i, fDetId+(type[0]<<8)+(type[1]<<9)+(type[2]<<10)+(i<<13));
} // end member function FillCableChMap

// add the data channel to the detector
void ETFDCTaL::Assign(const ETFHPTDCChannel *ch){
  const int uid = ETFDetectorManager::Instance()->GetUID(ch->GetChannelId());
  if(fDetId != (uid & 0xFF)) ETFMsg::Error("ETFDCTaL", "Assign: not from this DC.");
  const DCType xy = uid>>8 & 0x1 ? DCType::kY : DCType::kX;
  const int wireId = NL*(uid>>9 & 0x1) + 8*(uid>>10 & 0x7) + (uid>>13); // wireId: 0-80
  AddHit(ch, wireId, xy);
} // end member function Assign

void ETFDCTaL::AddHit(const ETFHPTDCChannel *ch, int wireId, DCType typeDC){
  int x12 = wireId / NL, id = wireId % NL;
  short xy = short(typeDC);
  char cc; // = 'x' or 'y' according to xy
  ETFDCHit *hit =
    dynamic_cast<ETFDCHit *>(fHits[xy][x12]->ConstructedAt(fHits[xy][x12]->GetEntries()));
  hit->Initialize();
  hit->SetDataChannel(ch);
  hit->SetLayerId(GetDCId()*2+x12); // 0-1-2-3-4-5: DC0X1-X2--DC1X1-X2--DC2X1-X2
  hit->SetWireId(id);
  hit->SetDelay(fDelay);
  // set position in local frame
  static const double x0 = -192.5; // 7.5-10.*20
  if(0 == xy){ // X
    cc = 'x';
    hit->SetX(x0+10.*id-5.*x12);
    hit->SetY(0.);
    hit->SetZ(10.*x12);
  } // end if
  if(1 == xy){ // Y
    cc = 'y';
    hit->SetX(0.);
    hit->SetY(x0+10.*id-5.*x12);
    hit->SetZ(10.*x12+25.);
  } // end if
  char name[128]; sprintf(name, "%s_%c%d_id%d", GetName(), cc, x12, id);
  hit->SetName(name);

  TransformToLabRef(hit);
  // set projection of the wire center position to the wire's normal plane
  SetHitProjection(hit, typeDC);
} // end member function AddHit

// add DC signal wires to fGDumb
void ETFDCTaL::ConstructGraph(){
  if(fGDumb->GetN()) return; // has called

  fGDumb->SetMarkerStyle(7); fGDumb->SetMarkerColor(15); // gray
  for(int i = 0; i < 2; i++) for(int j = 0; j < NL; j++){ // i: [0-1] -> [X1, X2]
    double p[3] = {10.*j-5.*i-192.5, 0., 10.*i}; // (x, y, z)
    TransformToLabRef(p);
    fGDumb->SetPoint(i*NL+j, p[2], p[0]);
  } // end for over i
} // end member function ConstructGraph

/// \retval false: if obtained drift time out of range
/// NOTE that if trk has non-FitStatus::kUNFITTED FitStatus, this method
/// would do nothing, as drift distance only changes with drift time,
/// whose changes would ensure fFitStatus be set to FitStatus::kUNFITTED
bool ETFDCTaL::SetDriftDistance(ETFVTrack *trk){
  // drift distance already assigned
  if(trk->GetFitStatus() != FitStatus::kUNFITTED) return true;

  for(auto &p : trk->GetHits()){
    ETFDCHit *h = p.second;
    // TODO: user input configuration expected here to confine drift time range
    if(!ETFMath::Within(h->GetDriftTime(), -30., 350.)) return false; // false hit
    h->SetDriftDistance(SpaceTimeRelation(h->GetDriftTime()));
  } // end for over hits
  return true;
} // end member function SetDriftDistance

// r = r(t); drift distance - drift time relation
double ETFDCTaL::SpaceTimeRelation(double t){
  // note that the following parameterization is for HV = 1030V
  // usually STR at other HVs are so different as worth special treatment
  // be it a garfield simulation, or user-refined tuning based on experiment data analysis
  static constexpr int POLN = 6;
  static const double p[POLN] =
    {-0.0687818, 0.0892494, -0.0011655, 1.0439e-05, -4.71409e-08, 8.07863e-11};
  double r = 0.;
  t = t/225.*150.;
  if(ETFMath::Within(t, 0., 190.)) for(int i = POLN; i--;) r += p[i]*pow(t, i);
  if(r < 0. || t < -10.) r = 0.;
  if(r > 5. || t > 190.) r = 5.;

  return r < 0. ? 0. : r;

  return r;
} // end member function SpaceTimeRelation
